home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr48 / eev100r1.zip / INFIX.PAS < prev    next >
Pascal/Delphi Source File  |  1993-04-01  |  47KB  |  1,415 lines

  1. Unit Infix;
  2.  
  3. { ------------------------------------------------------------------------
  4.   INFIX.PAS
  5.   ------------------------------------------------------------------------
  6.  
  7.     This unit uses recursive descent to evaluate expressions
  8.     written in infix notation.  The operations addition (+),
  9.     subtraction (-), multiplication (*), and division (/) are supported,
  10.     as are the functions ABS, ARCTAN, COS, EXP, LN, SQR, and SQRT.
  11.     PI returns the value for pi.  Results exceeding 1.0E37 are reported
  12.     as overflows.  Results less than 1.0E-37 are set to zero.
  13.  
  14.          Written by:
  15.  
  16.          James L. Dean
  17.          406 40th Street
  18.          New Orleans, LA 70124
  19.          February 25, 1985
  20.  
  21.          Modified by:
  22.  
  23.          David J. Firth
  24.          5665-A2 Parkville St.
  25.          Columbus, OH 43229
  26.          December 26, 1991
  27.  
  28.      This code was originally written as a stand-alone program using
  29.      standard Pascal.  In that form the program wasn't very useful.
  30.      I have taken the code and reorganized it for use with Turbo Pascal
  31.      versions 5.x or 6.0.  In addition, I have reworked it to support
  32.      variables by adding a preprocessor.  The variables are preceded and
  33.      followed by a @ symbol, are case sensitive, and must be less than
  34.      20 characters long (including the 2 @s). For example, the
  35.      following would all be valid variables:
  36.  
  37.      @VARIABLE1@      @Pressure3@      @AngleOfAttack@
  38.  
  39.      Variable identifiers are passed around as strings.
  40.  
  41.      Calculation results may either be stored in variables or returned
  42.      raw to the caller.  Raw calculations may not contain variables,
  43.      since the raw procedure calls are sent directly to the original
  44.      code.
  45.  
  46.      As a final note, the original code is virtually unreadable due
  47.      to the original author's lack of any comments.  I have attempted
  48.      to provide a front end to this code that is useful and understandable.
  49.  
  50.      Your comments are welcome (and desired!). My E-Mail addresses
  51.      are:
  52.  
  53.      GEnie:     D.FIRTH
  54.      CIS:       76467,1734
  55. }
  56.  
  57. Interface
  58.  
  59. type
  60.  
  61.   Str20 = string[20];                 {store variable IDs this way to conserve}
  62.  
  63.   VariablePtr = ^VariableType;        {for dynamic allocation of records }
  64.  
  65.   VariableType = record
  66.     ID    : Str20;                    {the id of the variable, with @s   }
  67.     Value : real;                     {the current value of the variable }
  68.     Next  : VariablePtr;              {hook to next record in linked list}
  69.   end; {VariableType}
  70.  
  71. var
  72.  
  73.   HPtr,                               {head of variable list       }
  74.   TPtr,                               {tail of variable list       }
  75.   SPtr  : VariablePtr;                {used to search variable list}
  76.  
  77.   CalcError : integer;                {the position of the error   }
  78.  
  79. procedure StoreVariable(VariableID:str20;MyValue:real);
  80. procedure ReadVariable(VariableID:str20;var MyValue:real;var MyError:boolean);
  81. procedure DestroyList;
  82.  
  83. procedure RawCalculate(MyFormula:string;var MyResult:real;var MyError:byte);
  84. procedure Calculate(MyFormula:string;var MyResult:real;var MyError:byte);
  85. procedure CalcAndStore(MyFormula:string;StoreID:str20;var MyError:byte);
  86.  
  87. Implementation
  88.  
  89. { ------------------------------------------------------------------------ }
  90.  
  91.   TYPE
  92.  
  93.     argument_record_ptr = ^argument_record;
  94.  
  95.     argument_record = RECORD
  96.                         value : REAL;
  97.                         next_ptr : argument_record_ptr
  98.                       END;
  99.  
  100.     string_1 = STRING[1];
  101.  
  102.     string_255 = STRING[255];
  103.  
  104.   VAR
  105.  
  106.     error_detected              : BOOLEAN;
  107.     error_msg                   : string_255;  
  108.     expression                  : string_255; 
  109.     expression_index            : INTEGER;       
  110.     expression_length           : INTEGER;
  111.     result                      : REAL;
  112.  
  113. { ------------------------------------------------------------------------ }
  114.  
  115.   PROCEDURE set_error(msg : string_255);
  116.     BEGIN
  117.       error_detected:=TRUE;
  118.       error_msg
  119.        :='Error:  '+msg+'.'
  120.     END;
  121.  
  122. { ------------------------------------------------------------------------ }
  123.  
  124.   PROCEDURE eat_leading_spaces;
  125.     VAR
  126.       non_blank_found           : BOOLEAN;
  127.     BEGIN
  128.       non_blank_found:=FALSE;
  129.       WHILE((expression_index <= expression_length)
  130.       AND   (NOT non_blank_found)) DO
  131.         IF expression[expression_index] = ' ' THEN
  132.           expression_index:=expression_index+1
  133.         ELSE
  134.           non_blank_found:=TRUE
  135.     END;
  136.  
  137. { ------------------------------------------------------------------------ }
  138.  
  139.   FUNCTION unsigned_integer : REAL;
  140.     VAR
  141.       non_digit_found           : BOOLEAN;
  142.       overflow                  : BOOLEAN;
  143.       result                    : REAL;
  144.       tem_char                  : CHAR;
  145.       tem_real                  : REAL;
  146.     BEGIN
  147.       non_digit_found:=FALSE;
  148.       result:=0.0;
  149.       overflow:=FALSE;
  150.       REPEAT
  151.         tem_char:=expression[expression_index];
  152.         IF ((tem_char >= '0') AND (tem_char <= '9')) THEN
  153.           BEGIN
  154.             tem_real:=ORD(tem_char)-ORD('0');
  155.             IF result > 1.0E36 THEN
  156.               overflow:=TRUE
  157.             ELSE
  158.               BEGIN
  159.                 result:=10.0*result+tem_real;
  160.                 expression_index:=expression_index+1;
  161.                 IF expression_index > expression_length THEN
  162.                   non_digit_found:=TRUE
  163.               END
  164.           END
  165.         ELSE
  166.           non_digit_found:=TRUE
  167.       UNTIL ((non_digit_found) OR (overflow));
  168.       IF overflow THEN
  169.         set_error('constant is too big');
  170.       unsigned_integer:=result
  171.     END;
  172.  
  173. { ------------------------------------------------------------------------ }
  174.  
  175.   FUNCTION unsigned_number : REAL;
  176.     VAR
  177.       exponent_value            : REAL;
  178.       exponent_sign             : CHAR;
  179.       factor                    : REAL;
  180.       non_digit_found           : BOOLEAN;
  181.       result                    : REAL;
  182.       tem_char                  : CHAR;
  183.       tem_real_1                : REAL;
  184.       tem_real_2                : REAL;
  185.     BEGIN
  186.       result:=unsigned_integer;
  187.       IF (NOT error_detected) THEN
  188.         BEGIN
  189.           IF expression_index <= expression_length THEN
  190.             BEGIN
  191.               tem_char:=expression[expression_index];
  192.               IF tem_char = '.' THEN
  193.                 BEGIN
  194.                   tem_real_1:=result;
  195.                   expression_index:=expression_index+1;
  196.                   IF expression_index > expression_length THEN
  197.                     set_error(
  198.             'end of expression encountered where decimal part expected')
  199.                   ELSE
  200.                     BEGIN
  201.                       tem_char:=expression[expression_index];
  202.                       IF ((tem_char >= '0') AND (tem_char <= '9')) THEN
  203.                         BEGIN
  204.                           factor:=1.0;
  205.                           non_digit_found:=FALSE;
  206.                           WHILE (NOT non_digit_found) DO
  207.                             BEGIN
  208.                               factor:=factor/10.0;
  209.                               tem_real_2:=ORD(tem_char)-ORD('0');
  210.                               tem_real_1:=tem_real_1+factor*tem_real_2;
  211.                               expression_index:=expression_index+1;
  212.                               IF expression_index > expression_length THEN
  213.                                non_digit_found:=TRUE
  214.                               ELSE
  215.                                 BEGIN
  216.                                   tem_char
  217.                                    :=expression[expression_index];
  218.                                   IF ((tem_char < '0')
  219.                                   OR  (tem_char > '9')) THEN
  220.                                     non_digit_found:=TRUE
  221.                                 END
  222.                             END;
  223.                           result:=tem_real_1
  224.                         END
  225.                       ELSE
  226.                         set_error(
  227.                          'decimal part of real number is missing')
  228.                     END
  229.                 END;
  230.               IF (NOT error_detected) THEN
  231.                 BEGIN
  232.                   IF expression_index <= expression_length THEN
  233.                     BEGIN
  234.                       IF ((tem_char = 'e') OR (tem_char = 'E')) THEN
  235.                         BEGIN
  236.                           expression_index:=expression_index+1;
  237.                           IF expression_index > expression_length THEN
  238.                             set_error(
  239.                'end of expression encountered where exponent expected')
  240.                          ELSE
  241.                             BEGIN
  242.                               tem_char
  243.                                :=expression[expression_index];
  244.                               IF ((tem_char = '+')
  245.                               OR  (tem_char = '-')) THEN
  246.                                 BEGIN
  247.                                   exponent_sign:=tem_char;
  248.                                   expression_index:=expression_index+1
  249.                                 END
  250.                               ELSE
  251.                                 exponent_sign:=' ';
  252.                               IF expression_index > expression_length
  253.                                THEN
  254.                                 set_error(
  255.      'end of expression encountered where exponent magnitude expected')
  256.                               ELSE
  257.                                 BEGIN
  258.                                   tem_char:=expression[expression_index];
  259.                                  IF ((tem_char >= '0')
  260.                                   AND (tem_char <= '9')) THEN
  261.                                     BEGIN
  262.                                       exponent_value
  263.                                        :=unsigned_integer;
  264.                                       IF (NOT error_detected) THEN
  265.                                         BEGIN
  266.                                           IF exponent_value > 37.0 THEN
  267.                                             set_error(
  268.                                    'magnitude of exponent is too large')
  269.                                           ELSE
  270.                                             BEGIN
  271.                                               tem_real_1:=1.0;
  272.                                               WHILE (exponent_value > 0.0) DO
  273.                                                 BEGIN
  274.                                                   exponent_value
  275.                                                    :=exponent_value-1.0;
  276.                                                   tem_real_1:=10.0*tem_real_1
  277.                                                 END;
  278.                                               IF exponent_sign = '-' THEN
  279.                                                tem_real_1
  280.                                                 :=1.0/tem_real_1;
  281.                                               IF result <> 0.0 THEN
  282.                                                 BEGIN
  283.                                                   tem_real_2
  284.                                                    :=(LN(tem_real_1)
  285.                                                    +LN(ABS(result)))
  286.                                                    /LN(10.0);
  287.                                                   IF tem_real_2 < -37.0 THEN
  288.                                                     result:=0.0
  289.                                                   ELSE
  290.                                                     IF tem_real_2 > 37.0 THEN
  291.                                                       set_error(
  292.                                                        'constant is too big')
  293.                                                     ELSE
  294.                                                       result:=result*tem_real_1
  295.                                                 END
  296.                                             END
  297.                                         END
  298.                                     END
  299.                                   ELSE
  300.                                     set_error(
  301.                                      'nonnumeric exponent encountered')
  302.                                 END
  303.                             END
  304.                         END
  305.                     END
  306.                 END
  307.             END
  308.         END;
  309.       unsigned_number:=result
  310.     END;
  311.  
  312. { ------------------------------------------------------------------------ }
  313.  
  314.   FUNCTION pop_argument(VAR argument_stack_head : argument_record_ptr) : REAL;
  315.     VAR
  316.       argument_stack_ptr        : argument_record_ptr;
  317.       result                    : REAL;
  318.     BEGIN
  319.       result
  320.        :=argument_stack_head^.value;
  321.       argument_stack_ptr
  322.        :=argument_stack_head^.next_ptr;
  323.       DISPOSE(argument_stack_head);
  324.       argument_stack_head:=argument_stack_ptr;
  325.       pop_argument:=result
  326.     END;
  327.  
  328. { ------------------------------------------------------------------------ }
  329.  
  330.   FUNCTION abs_function(VAR argument_stack_head : argument_record_ptr;
  331.    VAR function_name : string_255) : REAL;
  332.     VAR
  333.       argument                  : REAL;
  334.       result                    : REAL;
  335.     BEGIN
  336.       result:=0.0;
  337.       IF argument_stack_head = NIL THEN
  338.         set_error(
  339.          'argument to "'+function_name+'" is missing')
  340.       ELSE
  341.         BEGIN
  342.           argument:=pop_argument(argument_stack_head);
  343.           IF argument_stack_head = NIL THEN
  344.             IF argument >= 0.0 THEN
  345.               result:=argument
  346.             ELSE
  347.               result:=-argument
  348.           ELSE
  349.             set_error(
  350.              'extraneous argument supplied to function "'+
  351.              function_name+'"')
  352.         END;
  353.       abs_function:=result
  354.     END;
  355.  
  356. { ------------------------------------------------------------------------ }
  357.  
  358.   FUNCTION arctan_function(VAR argument_stack_head : argument_record_ptr;
  359.    VAR function_name : string_255) : REAL;
  360.     VAR
  361.       argument                  : REAL;
  362.       result                    : REAL;
  363.     BEGIN
  364.       result:=0.0;
  365.       IF argument_stack_head = NIL THEN
  366.        set_error(
  367.         'argument to "'+function_name+'" is missing')
  368.       ELSE
  369.         BEGIN
  370.           argument:=pop_argument(argument_stack_head);
  371.           IF argument_stack_head = NIL THEN
  372.             result:=ARCTAN(argument)
  373.           ELSE
  374.             set_error(
  375.              'extraneous argument supplied to function "'+
  376.              function_name+'"')
  377.         END;
  378.       arctan_function:=result
  379.     END;
  380.  
  381. { ------------------------------------------------------------------------ }
  382.  
  383.   FUNCTION cos_function(VAR argument_stack_head : argument_record_ptr;
  384.    VAR function_name : string_255) : REAL;
  385.     VAR
  386.       argument                  : REAL;
  387.       result                    : REAL;
  388.     BEGIN
  389.       result:=0.0;
  390.       IF argument_stack_head = NIL THEN
  391.         set_error(
  392.          'argument to "'+function_name+'" is missing')
  393.       ELSE
  394.         BEGIN
  395.           argument:=pop_argument(argument_stack_head);
  396.           IF argument_stack_head = NIL THEN
  397.             result:=COS(argument)
  398.           ELSE
  399.             set_error(
  400.              'extraneous argument supplied to function "'+
  401.              function_name+'"')
  402.         END;
  403.       cos_function:=result
  404.     END;
  405.  
  406. { ------------------------------------------------------------------------ }
  407.  
  408.   FUNCTION exp_function(VAR argument_stack_head : argument_record_ptr;
  409.    VAR function_name : string_255) : REAL;
  410.     VAR
  411.       argument                  : REAL;
  412.       result                    : REAL;
  413.       tem_real                  : REAL;
  414.     BEGIN
  415.       result:=0.0;
  416.       IF argument_stack_head = NIL THEN
  417.         set_error(
  418.          'argument to "'+function_name+'" is missing')
  419.       ELSE
  420.         BEGIN
  421.           argument:=pop_argument(argument_stack_head);
  422.           IF argument_stack_head = NIL THEN
  423.             BEGIN
  424.               tem_real:=argument/LN(10.0);
  425.               IF tem_real < -37.0 THEN
  426.                 result:=0.0
  427.               ELSE
  428.                 IF tem_real > 37.0 THEN
  429.                   set_error(
  430.                    'overflow detected while calculating "'+
  431.                    function_name+'"')
  432.                 ELSE
  433.                   result:=EXP(argument)
  434.             END
  435.           ELSE
  436.             set_error(
  437.              'extraneous argument supplied to function "'+
  438.              function_name+'"')
  439.         END;
  440.       exp_function:=result
  441.     END;
  442.  
  443. { ------------------------------------------------------------------------ }
  444.  
  445.   FUNCTION ln_function(VAR argument_stack_head : argument_record_ptr;
  446.    VAR function_name : string_255) : REAL;
  447.     VAR
  448.       argument                  : REAL;
  449.       result                    : REAL;
  450.     BEGIN
  451.       result:=0.0;
  452.       IF argument_stack_head = NIL THEN
  453.         set_error(
  454.          'argument to "'+function_name+'" is missing')
  455.       ELSE
  456.         BEGIN
  457.           argument:=pop_argument(argument_stack_head);
  458.           IF argument_stack_head = NIL THEN
  459.             IF argument <= 0.0 THEN
  460.               set_error(
  461.                'argument to "'+function_name+
  462.                '" is other than positive')
  463.             ELSE
  464.               result:=LN(argument)
  465.           ELSE
  466.             set_error(
  467.              'extraneous argument supplied to function "'+
  468.              function_name+'"')
  469.         END;
  470.       ln_function:=result
  471.     END;
  472.  
  473. { ------------------------------------------------------------------------ }
  474.  
  475.   FUNCTION pi_function(VAR argument_stack_head : argument_record_ptr;
  476.    VAR function_name : string_255) : REAL;
  477.     VAR
  478.       argument                  : REAL;
  479.       result                    : REAL;
  480.     BEGIN
  481.       result:=0.0;
  482.       IF argument_stack_head = NIL THEN
  483.         result:=4.0*ARCTAN(1.0)
  484.       ELSE
  485.         set_error(
  486.          'extraneous argument supplied to function "'+
  487.          function_name+'"');
  488.       pi_function:=result
  489.     END;
  490.  
  491. { ------------------------------------------------------------------------ }
  492.  
  493.   FUNCTION sin_function(VAR argument_stack_head : argument_record_ptr;
  494.    VAR function_name : string_255) : REAL;
  495.     VAR
  496.       argument                  : REAL;
  497.       result                    : REAL;
  498.     BEGIN
  499.       result:=0.0;
  500.       IF argument_stack_head = NIL THEN
  501.         set_error(
  502.          'argument to "'+function_name+'" is missing')
  503.       ELSE
  504.         BEGIN
  505.           argument:=pop_argument(argument_stack_head);
  506.           IF argument_stack_head = NIL THEN
  507.             result:=SIN(argument)
  508.           ELSE
  509.             set_error(
  510.              'extraneous argument supplied to function "'+
  511.              function_name+'"')
  512.         END;
  513.       sin_function:=result
  514.     END;
  515.  
  516. { ------------------------------------------------------------------------ }
  517.  
  518.   FUNCTION sqr_function(VAR argument_stack_head : argument_record_ptr;
  519.    VAR function_name : string_255) : REAL;
  520.     VAR
  521.       argument                  : REAL;
  522.       result                    : REAL;
  523.       tem_real                  : REAL;
  524.     BEGIN
  525.       result:=0.0;
  526.       IF argument_stack_head = NIL THEN
  527.         set_error(
  528.          'argument to "'+function_name+'" is missing')
  529.       ELSE
  530.         BEGIN
  531.           argument:=pop_argument(argument_stack_head);
  532.           IF argument_stack_head = NIL THEN
  533.             IF argument = 0.0 THEN
  534.               result:=0.0
  535.             ELSE
  536.               BEGIN
  537.                 tem_real:=2.0*LN(ABS(argument))/LN(10.0);
  538.                 IF tem_real < -37.0 THEN
  539.                   result:=0.0
  540.                 ELSE
  541.                   IF tem_real > 37.0 THEN
  542.                     set_error(
  543.                      'overflow detected during calculation of "'+
  544.                      function_name+'"')
  545.                   ELSE
  546.                     result:=argument*argument
  547.               END
  548.           ELSE
  549.             set_error(
  550.              'extraneous argument supplied to function "'+
  551.              function_name+'"')
  552.         END;
  553.       sqr_function:=result
  554.     END;
  555.  
  556. { ------------------------------------------------------------------------ }
  557.  
  558.   FUNCTION sqrt_function(VAR argument_stack_head : argument_record_ptr;
  559.    VAR function_name : string_255) : REAL;
  560.     VAR
  561.       argument                  : REAL;
  562.       result                    : REAL;
  563.     BEGIN
  564.       result:=0.0;
  565.       IF argument_stack_head = NIL THEN
  566.         set_error(
  567.          'argument to "'+function_name+'" is missing')
  568.       ELSE
  569.         BEGIN
  570.           argument:=pop_argument(argument_stack_head);
  571.           IF argument_stack_head = NIL THEN
  572.             IF argument < 0.0 THEN
  573.               set_error(
  574.                'argument to "'+function_name+
  575.                '" is negative')
  576.             ELSE
  577.               result:=SQRT(argument)
  578.           ELSE
  579.             set_error(
  580.              'extraneous argument supplied to function "'+
  581.              function_name+'"')
  582.         END;
  583.       sqrt_function:=result
  584.     END;
  585.  
  586. { ------------------------------------------------------------------------ }
  587.  
  588.   FUNCTION simple_expression : REAL; FORWARD;
  589.  
  590. { ------------------------------------------------------------------------ }
  591.  
  592.   FUNCTION funct : REAL;
  593.     VAR
  594.       argument                  : REAL;
  595.       argument_stack_head       : argument_record_ptr;
  596.       argument_stack_ptr        : argument_record_ptr;
  597.       arguments_okay            : BOOLEAN;
  598.       function_name             : string_255;
  599.       non_alphanumeric_found    : BOOLEAN;
  600.       result                    : REAL;
  601.       right_parenthesis_found   : BOOLEAN;
  602.       tem_char                  : CHAR;
  603.     BEGIN    
  604.       result:=0.0;
  605.       non_alphanumeric_found:=FALSE;
  606.       function_name:='';
  607.       WHILE((expression_index <= expression_length)
  608.       AND   (NOT non_alphanumeric_found)) DO
  609.         BEGIN
  610.           tem_char:=expression[expression_index];
  611.           tem_char:=UPCASE(tem_char);
  612.           IF ((tem_char >= 'A') AND (tem_char <= 'Z')) THEN
  613.             BEGIN
  614.               function_name:=function_name+tem_char;
  615.               expression_index:=expression_index+1
  616.             END
  617.           ELSE
  618.             non_alphanumeric_found:=TRUE
  619.         END;
  620.       argument_stack_head:=NIL;
  621.       arguments_okay:=TRUE;
  622.       eat_leading_spaces;
  623.       IF expression_index <= expression_length THEN
  624.         BEGIN
  625.           tem_char:=expression[expression_index];
  626.           IF tem_char = '(' THEN
  627.             BEGIN
  628.               expression_index:=expression_index+1;
  629.               right_parenthesis_found:=FALSE;
  630.               WHILE ((NOT right_parenthesis_found)
  631.               AND    (arguments_okay)
  632.               AND    (expression_index <= expression_length)) DO
  633.                 BEGIN
  634.                   argument:=simple_expression;
  635.                   IF error_detected THEN
  636.                     arguments_okay:=FALSE
  637.                   ELSE
  638.                     BEGIN
  639.                       IF argument_stack_head = NIL THEN
  640.                         BEGIN
  641.                           NEW(argument_stack_head);
  642.                           argument_stack_head^.value:=argument;
  643.                           argument_stack_head^.next_ptr:=NIL
  644.                         END
  645.                       ELSE
  646.                         BEGIN
  647.                           NEW(argument_stack_ptr);
  648.                           argument_stack_ptr^.value:=argument;
  649.                           argument_stack_ptr^.next_ptr
  650.                            :=argument_stack_head;
  651.                           argument_stack_head:=argument_stack_ptr
  652.                         END;
  653.                       eat_leading_spaces;
  654.                       IF expression_index <= expression_length THEN
  655.                         BEGIN
  656.                           tem_char:=expression[expression_index];
  657.                           IF tem_char = ')' THEN
  658.                             BEGIN
  659.                               right_parenthesis_found:=TRUE;
  660.                               expression_index:=expression_index+1
  661.                             END
  662.                           ELSE
  663.                             IF tem_char = ',' THEN
  664.                               expression_index:=expression_index+1
  665.                             ELSE
  666.                               BEGIN
  667.                                 arguments_okay:=FALSE;
  668.                                 set_error(
  669.                             'comma missing from function arguments')
  670.                               END
  671.                         END
  672.                     END
  673.                 END;
  674.               IF arguments_okay THEN
  675.                 BEGIN
  676.                   IF (NOT right_parenthesis_found) THEN
  677.                     BEGIN
  678.                       arguments_okay:=FALSE;
  679.                       set_error(
  680.                    '")" to terminate function arguments is missing')
  681.                     END
  682.                 END
  683.             END
  684.         END;
  685.       IF arguments_okay THEN
  686.         BEGIN
  687.           IF function_name = 'ABS' THEN
  688.             result
  689.              :=abs_function(argument_stack_head,function_name) 
  690.           ELSE
  691.             IF function_name = 'ARCTAN' THEN
  692.               result
  693.                :=arctan_function(argument_stack_head,function_name)
  694.             ELSE
  695.               IF function_name = 'COS' THEN
  696.                 result
  697.                  :=cos_function(argument_stack_head,function_name)
  698.               ELSE
  699.                 IF function_name = 'EXP' THEN
  700.                   result
  701.                    :=exp_function(argument_stack_head,function_name)
  702.                 ELSE
  703.                   IF function_name = 'LN' THEN
  704.                     result
  705.                      :=ln_function(argument_stack_head,function_name)
  706.                   ELSE
  707.                     IF function_name = 'PI' THEN
  708.                       result
  709.                        :=pi_function(argument_stack_head,function_name)
  710.                     ELSE
  711.                       IF function_name = 'SIN' THEN
  712.                         result
  713.                          :=sin_function(argument_stack_head,function_name)
  714.                       ELSE
  715.                         IF function_name = 'SQR' THEN
  716.                           result
  717.                            :=sqr_function(argument_stack_head,function_name)
  718.                         ELSE
  719.                           IF function_name = 'SQRT' THEN
  720.                             result
  721.                              :=sqrt_function(argument_stack_head,function_name)
  722.                           ELSE
  723.                             set_error('the function "'+
  724.                              function_name+'" is unrecognized')
  725.         END;
  726.       WHILE (argument_stack_head <> NIL) DO
  727.         BEGIN
  728.           argument_stack_ptr:=argument_stack_head^.next_ptr;
  729.           DISPOSE(argument_stack_head);
  730.           argument_stack_head:=argument_stack_ptr
  731.         END;
  732.       funct:=result
  733.     END;
  734.  
  735. { ------------------------------------------------------------------------ }
  736.  
  737.   FUNCTION factor : REAL;
  738.     VAR
  739.       result                    : REAL;
  740.       tem_char                  : CHAR;
  741.     BEGIN
  742.       result:=0.0;
  743.       eat_leading_spaces;
  744.       IF expression_index > expression_length THEN
  745.         set_error(
  746.          'end of expression encountered where factor expected')
  747.       ELSE
  748.         BEGIN
  749.           tem_char:=expression[expression_index];
  750.           BEGIN
  751.             IF tem_char = '(' THEN
  752.               BEGIN
  753.                 expression_index:=expression_index+1;
  754.                 result:=simple_expression;
  755.                 IF (NOT error_detected) THEN
  756.                   BEGIN
  757.                     eat_leading_spaces;
  758.                     IF expression_index > expression_length THEN
  759.                       set_error(
  760.                        'end of expression encountered '+
  761.                        'where ")" was expected')
  762.                     ELSE
  763.                       IF expression[expression_index] = ')' THEN
  764.                         expression_index:=expression_index+1
  765.                       ELSE
  766.                         set_error('expression not followed by ")"')
  767.                   END
  768.               END
  769.             ELSE
  770.               IF ((tem_char >= '0') AND (tem_char <= '9')) THEN
  771.                 result:=unsigned_number
  772.               ELSE
  773.                 IF (((tem_char >= 'a') AND (tem_char <= 'z'))
  774.                 OR  ((tem_char >= 'A') AND (tem_char <= 'Z'))) THEN
  775.                   result:=funct
  776.                 ELSE
  777.                   set_error(
  778.                    'function, unsigned number, or "(" expected')
  779.           END
  780.         END;
  781.       factor:=result
  782.     END;
  783.  
  784. { ------------------------------------------------------------------------ }
  785.  
  786.   FUNCTION quotient_of_factors(VAR left_value,right_value : REAL) : REAL;
  787.     VAR
  788.       result                    : REAL;
  789.       tem_real                  : REAL;
  790.     BEGIN
  791.       result:=0.0;
  792.       IF right_value = 0.0 THEN
  793.         set_error('division by zero attempted')
  794.       ELSE
  795.         BEGIN
  796.           IF left_value = 0.0 THEN
  797.             result:=0.0
  798.           ELSE
  799.             BEGIN
  800.               tem_real:=(LN(ABS(left_value))-LN(ABS(right_value)))/LN(10.0);
  801.               IF tem_real < -37.0 THEN 
  802.                 result:=0.0
  803.               ELSE
  804.                 IF tem_real > 37.0 THEN
  805.                   set_error(
  806.                    'overflow detected during division')
  807.                 ELSE
  808.                   result:=left_value/right_value
  809.             END
  810.         END;
  811.       quotient_of_factors:=result
  812.     END;
  813.  
  814. { ------------------------------------------------------------------------ }
  815.  
  816.   FUNCTION product_of_factors(VAR left_value,right_value : REAL) : REAL;
  817.     VAR
  818.       result                    : REAL;
  819.       tem_real                  : REAL;
  820.     BEGIN
  821.       result:=0.0;
  822.       IF ((left_value <> 0.0) AND (right_value <> 0.0)) THEN
  823.         BEGIN
  824.           tem_real:=(LN(ABS(left_value))+LN(ABS(right_value)))/LN(10.0); 
  825.           IF tem_real < -37.0 THEN
  826.             result:=0.0
  827.           ELSE
  828.             IF tem_real > 37.0 THEN
  829.               set_error(
  830.                'overflow detected during multiplication')
  831.             ELSE
  832.               result:=left_value*right_value
  833.         END;
  834.       product_of_factors:=result
  835.     END;
  836.  
  837. { ------------------------------------------------------------------------ }
  838.  
  839.   FUNCTION factor_operator : string_1;
  840.     VAR
  841.       result                    : string_1;
  842.     BEGIN
  843.       eat_leading_spaces;
  844.       IF expression_index <= expression_length THEN
  845.         BEGIN
  846.           result:=expression[expression_index];
  847.           IF ((result = '*')
  848.           OR  (result = '/')) THEN
  849.             expression_index:=expression_index+1
  850.         END
  851.       ELSE
  852.         result:='';
  853.       factor_operator:=result
  854.     END;
  855.  
  856. { ------------------------------------------------------------------------ }
  857.  
  858.   FUNCTION term : REAL;
  859.     VAR
  860.       operator                  : string_1;
  861.       operator_found            : BOOLEAN;
  862.       result                    : REAL;
  863.       right_value               : REAL;
  864.     BEGIN
  865.       result:=0;
  866.       eat_leading_spaces;
  867.       IF expression_index > expression_length THEN
  868.         set_error(
  869.          'end of expression encountered where term was expected')
  870.       ELSE
  871.         BEGIN
  872.           result:=factor;
  873.           operator_found:=TRUE;
  874.           WHILE((NOT error_detected)
  875.           AND   (operator_found)) DO
  876.             BEGIN
  877.               operator:=factor_operator;
  878.               IF LENGTH(operator) = 0 THEN
  879.                 operator_found:=FALSE
  880.               ELSE
  881.                 IF ((operator <> '*')
  882.                 AND (operator <> '/')) THEN
  883.                   operator_found:=FALSE
  884.                 ELSE
  885.                   BEGIN
  886.                     right_value:=factor;
  887.                     IF (NOT error_detected) THEN
  888.                       BEGIN
  889.                         IF operator = '*' THEN
  890.                             result:=product_of_factors(
  891.                              result,right_value)
  892.                         ELSE
  893.                             result:=quotient_of_factors(
  894.                              result,right_value)
  895.                       END
  896.                   END
  897.             END
  898.         END;
  899.       term:=result
  900.     END;
  901.  
  902. { ------------------------------------------------------------------------ }
  903.  
  904.   FUNCTION sum_of_terms(VAR left_value,right_value : REAL) : REAL;
  905.     VAR
  906.       result                    : REAL;
  907.     BEGIN
  908.       result:=0.0;
  909.       IF ((left_value > 0.0) AND (right_value > 0.0)) THEN
  910.         IF left_value > (1.0E37 - right_value) THEN
  911.           set_error('overflow detected during addition')
  912.         ELSE
  913.           result:=left_value+right_value
  914.       ELSE
  915.         IF ((left_value < 0.0) AND (right_value < 0.0)) THEN
  916.           IF left_value < (-1.0E37 - right_value) THEN
  917.             set_error('overflow detected during addition')
  918.           ELSE
  919.             result:=left_value+right_value
  920.         ELSE
  921.           result:=left_value+right_value;
  922.       sum_of_terms:=result
  923.     END;
  924.  
  925. { ------------------------------------------------------------------------ }
  926.  
  927.   FUNCTION difference_of_terms(VAR left_value,right_value : REAL) : REAL;
  928.     VAR
  929.       result                    : REAL;
  930.     BEGIN
  931.       IF ((left_value < 0.0) AND (right_value > 0.0)) THEN
  932.         IF left_value < (right_value - 1.0E37) THEN
  933.           set_error('overflow detected during subtraction')
  934.         ELSE
  935.           result:=left_value-right_value
  936.       ELSE
  937.         IF ((left_value > 0.0) AND (right_value < 0.0)) THEN
  938.           IF left_value > (right_value + 1.0E37) THEN
  939.             set_error('overflow detected during subtraction')
  940.           ELSE
  941.             result:=left_value-right_value
  942.         ELSE
  943.           result:=left_value-right_value;
  944.       difference_of_terms:=result
  945.     END;
  946.  
  947. { ------------------------------------------------------------------------ }
  948.  
  949.   FUNCTION term_operator : string_1;
  950.     VAR
  951.       result                    : string_1;
  952.     BEGIN
  953.       eat_leading_spaces;
  954.       IF expression_index <= expression_length THEN
  955.         BEGIN
  956.           result:=expression[expression_index];
  957.           IF ((result = '+')
  958.           OR  (result = '-')) THEN
  959.             expression_index:=expression_index+1
  960.         END
  961.       ELSE
  962.         result:='';
  963.       term_operator:=result
  964.     END;
  965.  
  966. { ------------------------------------------------------------------------ }
  967.  
  968.   FUNCTION simple_expression;
  969.     VAR
  970.       leading_sign              : CHAR;
  971.       operator                  : string_1;
  972.       operator_found            : BOOLEAN;
  973.       result                    : REAL;
  974.       right_value               : REAL;
  975.       tem_char                  : CHAR;
  976.     BEGIN
  977.       result:=0.0;
  978.       eat_leading_spaces;
  979.       IF expression_index > expression_length THEN
  980.         set_error(
  981.        'end of expression encountered where simple expression expected')
  982.       ELSE
  983.         BEGIN
  984.           leading_sign:=' ';
  985.           tem_char:=expression[expression_index];
  986.           IF ((tem_char = '+') OR (tem_char = '-')) THEN
  987.             BEGIN
  988.               leading_sign:=tem_char;
  989.               expression_index:=expression_index+1
  990.             END;
  991.           result:=term;
  992.           IF (NOT error_detected) THEN
  993.             BEGIN
  994.               IF leading_sign <> ' ' THEN
  995.                 BEGIN
  996.                   IF leading_sign = '-' THEN
  997.                     result:=-result
  998.                 END;
  999.               operator_found:=TRUE;
  1000.               WHILE((NOT error_detected)
  1001.               AND   (operator_found)) DO
  1002.                 BEGIN
  1003.                   operator:=term_operator;
  1004.                   IF LENGTH(operator) = 0 THEN
  1005.                     operator_found:=FALSE
  1006.                   ELSE
  1007.                     IF ((operator <> '+')
  1008.                     AND (operator <> '-')) THEN
  1009.                       operator_found:=FALSE
  1010.                     ELSE
  1011.                       BEGIN
  1012.                         right_value:=term;
  1013.                         IF (NOT error_detected) THEN
  1014.                           BEGIN
  1015.                             IF operator = '+' THEN
  1016.                               result:=sum_of_terms(
  1017.                                result,right_value)
  1018.                             ELSE
  1019.                               result:=difference_of_terms(
  1020.                                result,right_value)
  1021.                           END
  1022.                       END
  1023.                 END
  1024.             END
  1025.         END;
  1026.       simple_expression:=result
  1027.     END;
  1028.  
  1029. { ------------------------------------------------------------------------ }
  1030.  
  1031.   PROCEDURE output_value(VAR result : REAL);
  1032.  
  1033.   { this procedure used to send text directly to the display.
  1034.     I reworked it to condition the value only and then return. }
  1035.  
  1036.     VAR
  1037.       digits_in_integer_part       : INTEGER;
  1038.       magnitude_of_result          : REAL;
  1039.  
  1040.     BEGIN
  1041.  
  1042.       IF result >= 0.0 THEN
  1043.         magnitude_of_result:=result
  1044.       ELSE
  1045.         magnitude_of_result:=-result;
  1046.       IF magnitude_of_result >= 5.0E-3 THEN
  1047.         BEGIN
  1048.           digits_in_integer_part:=0;
  1049.           WHILE ((digits_in_integer_part <= 8)
  1050.           AND    (magnitude_of_result >= 1.0)) DO
  1051.             BEGIN
  1052.               magnitude_of_result:=magnitude_of_result/10.0;
  1053.               digits_in_integer_part:=digits_in_integer_part+1
  1054.             END;
  1055. (*
  1056.           IF digits_in_integer_part > 8 THEN
  1057.             WRITELN(OUTPUT,result:13)
  1058.           ELSE
  1059.             WRITELN(OUTPUT,result:10:8-digits_in_integer_part)
  1060. *)
  1061.         END;
  1062. (*
  1063.       ELSE
  1064.         WRITELN(OUTPUT,result:13)
  1065. *)
  1066.     END;
  1067.  
  1068. { ------------------------------------------------------------------------ }
  1069.  
  1070.   PROCEDURE output_error(error_msg : string_255;
  1071.                          VAR expression : string_255;
  1072.                          VAR expression_index : INTEGER);
  1073.  
  1074.     { this routine used to write the expression, the position of
  1075.       the error, and an error message to the screen. it has been
  1076.       reworked to keep the position of the error only. if more
  1077.       information is required, add the code here. the original
  1078.       calling convention has been preserved.
  1079.     }
  1080.  
  1081.     BEGIN
  1082.  
  1083.       {trap the error here to see in Turbo Debugger}
  1084.  
  1085.       CalcError := expression_index;
  1086.  
  1087.     END;
  1088.  
  1089. { ------------------------------------------------------------------------ }
  1090.  
  1091. procedure RawCalculate(MyFormula:string;var MyResult:real;var MyError:byte);
  1092.  
  1093. { this procedure will evaluate an expression without variables.
  1094.   it is called by the Calculate procedure once variable values
  1095.   have been inserted into the expression.
  1096.  
  1097.   MyError will be 0 for a successful evaluation.
  1098. }
  1099.  
  1100. begin
  1101.  
  1102.   expression := MyFormula;
  1103.   MyResult := 0;
  1104.   CalcError := 0;
  1105.   expression_length := length(MyFormula);
  1106.  
  1107.   { ---- Original code starts here ---- }
  1108.  
  1109.   error_detected:=FALSE;
  1110.   expression_index:=1;
  1111.   result:=simple_expression;
  1112.  
  1113.   IF error_detected THEN
  1114.     output_error(error_msg,expression,expression_index)
  1115.   ELSE
  1116.     BEGIN
  1117.       eat_leading_spaces;
  1118.       IF expression_index <= expression_length THEN
  1119.         output_error('Error:  expression followed by garbage',
  1120.                      expression,expression_index)
  1121.       ELSE
  1122.         output_value(result);
  1123.     END;
  1124.  
  1125.   { ---- Original code ends here ---- }
  1126.  
  1127.   MyResult := result;
  1128.   MyError := CalcError;
  1129.  
  1130. end; {RawCalc}
  1131.  
  1132. { ------------------------------------------------------------------------ }
  1133.  
  1134. procedure GetPointerTo(VariableID:str20;var MPtr:VariablePtr);
  1135.  
  1136. var
  1137.  
  1138.   Done : boolean;
  1139.   XPtr : VariablePtr;
  1140.  
  1141. begin
  1142.  
  1143.   MPtr := nil;
  1144.   XPtr := HPtr;
  1145.  
  1146.   Done := false;
  1147.   while (not Done) do begin
  1148.  
  1149.     if XPtr^.ID=VariableID then
  1150.       MPtr := XPtr;
  1151.  
  1152.     if XPtr^.Next=nil then
  1153.       Done := true
  1154.     else
  1155.       XPtr := XPtr^.Next;
  1156.  
  1157.   end; {while}
  1158.  
  1159. end; {GetPointerTo}
  1160.  
  1161. { ------------------------------------------------------------------------ }
  1162.  
  1163. procedure ReadVariable(VariableID:str20;var MyValue:real;var MyError:boolean);
  1164.  
  1165. var
  1166.  
  1167.   MPtr : VariablePtr;
  1168.  
  1169. begin
  1170.  
  1171.   MyError := false;
  1172.   MyValue := 0;
  1173.  
  1174.   GetPointerTo(VariableID,MPtr);
  1175.  
  1176.   if MPtr<>nil then begin
  1177.     MyValue := MPtr^.Value
  1178.   end
  1179.   else begin
  1180.     MyError := true;
  1181.   end;
  1182.  
  1183. end; {ReadVariable}
  1184.  
  1185. { ------------------------------------------------------------------------ }
  1186.  
  1187. procedure StoreVariable(VariableID:str20;MyValue:real);
  1188.  
  1189. var
  1190.  
  1191.   WorkingRec : VariableType;
  1192.  
  1193. begin
  1194.  
  1195.   fillchar(WorkingRec,sizeof(WorkingRec),0);
  1196.   WorkingRec.ID := VariableID;
  1197.   WorkingRec.Value := MyValue;
  1198.  
  1199.   If HPtr = nil then begin
  1200.  
  1201.     {this is the first record added to the list}
  1202.  
  1203.     New(HPtr);                                {allocate 1st record in LL }
  1204.     TPtr := HPtr;                             {init tail (= head)        }
  1205.     TPtr^ := WorkingRec;                      {add new record as head    }
  1206.     TPtr^.Next := nil;                        {set the next link for tail}
  1207.  
  1208.   end
  1209.   else begin
  1210.  
  1211.     GetPointerTo(VariableID,SPtr);
  1212.  
  1213.     if SPtr <> nil then begin
  1214.  
  1215.       {the list exists and so does the variable -- modify value}
  1216.  
  1217.       SPtr^.Value := MyValue;
  1218.  
  1219.     end
  1220.     else begin
  1221.  
  1222.       {the list exists, but the variable doesn't -- add it}
  1223.  
  1224.       New(SPtr);                          {allocate new record for LL }
  1225.       SPtr^ := WorkingRec;                {put info in new LL record  }
  1226.       TPtr^.Next := SPtr;                 {add new record as tail     }
  1227.       SPtr^.Next := nil;                  {set the new link for tail  }
  1228.       TPtr := SPtr;                       {point tail to new record   }
  1229.  
  1230.     end; {if-else}
  1231.  
  1232.   end;
  1233.  
  1234. end; {StoreVariable}
  1235.  
  1236. { ------------------------------------------------------------------------- }
  1237.  
  1238. Procedure DestroyFieldList(TempPtr:VariablePtr);
  1239.  
  1240. { This procedure recursively destroys a linked list }
  1241.  
  1242. Begin
  1243.  
  1244.   If TempPtr^.Next <> nil then
  1245.     DestroyFieldList(TempPtr^.Next);
  1246.  
  1247.   Dispose(TempPtr);
  1248.  
  1249. End;
  1250.  
  1251. { ------------------------------------------------------------------------ }
  1252.  
  1253. procedure DestroyList;
  1254.  
  1255. begin
  1256.  
  1257.   if HPtr <> Nil then
  1258.     DestroyFieldList(HPtr);
  1259.  
  1260.   HPtr := nil;
  1261.   TPtr := nil;
  1262.   SPtr := nil;
  1263.  
  1264. end; {DestroyList}
  1265.  
  1266. { ------------------------------------------------------------------------ }
  1267.  
  1268. procedure Calculate(MyFormula:string;var MyResult:real;var MyError:byte);
  1269.  
  1270. { this procedure will evaluate an expression containing variables.
  1271.   this routine will scan the expression for variables, removing
  1272.   the variable IDs and substituting the value into the expression.
  1273.   once all variable IDs have been removed, this procedure calls
  1274.   RawCalculate for expression evaluation.
  1275.  
  1276.   MyError will be 0 for a successful evaluation.
  1277. }
  1278.  
  1279. var
  1280.  
  1281.   VarStr,
  1282.   DestStr : string;
  1283.   Index   : byte;
  1284.   MyReal  : real;
  1285.   MyErr   : boolean;
  1286.  
  1287. begin
  1288.  
  1289.   {the first part of this routine is the preprocessor for variables.
  1290.    the formula string will be copied to another string. as the string
  1291.    is copied, values for any variables will be inserted where the
  1292.    variable ID was in the original string.}
  1293.  
  1294.   MyError := 0;
  1295.   DestStr := '';
  1296.   Index := 1;
  1297.  
  1298.   while Index <= length(MyFormula) do begin
  1299.  
  1300.     if MyFormula[Index]='@' then begin
  1301.  
  1302.       VarStr := '@';
  1303.       inc(Index);
  1304.       while (MyFormula[Index]<>'@') AND (Index<=length(MyFormula)) do begin
  1305.         VarStr := VarStr + MyFormula[Index];
  1306.         inc(Index);
  1307.       end; {while}
  1308.       VarStr := VarStr + '@';
  1309.  
  1310.       if VarStr[length(VarStr)]='@' then begin
  1311.         {read variable}
  1312.         ReadVariable(VarStr,MyReal,MyErr);
  1313.         if not MyErr then begin
  1314.           {substitute value for variable}
  1315.           str(MyReal,VarStr);
  1316.           DestStr := DestStr + VarStr;
  1317.         end
  1318.         else
  1319.           {didn't find variable}
  1320.           MyError := Index - length(VarStr);
  1321.       end
  1322.       else begin
  1323.         {ran out of formula!}
  1324.         MyError := Index - length(VarStr);
  1325.       end; {if-else}
  1326.  
  1327.     end
  1328.     else
  1329.       DestStr := DestStr + MyFormula[Index];
  1330.  
  1331.     inc(Index);
  1332.  
  1333.   end; {while}
  1334.  
  1335.   if MyError=0 then begin
  1336.     MyFormula := DestStr;
  1337.     {call RawCalculate to evaluate expression}
  1338.     RawCalculate(MyFormula,MyResult,MyError);
  1339.   end;
  1340.  
  1341. end; {Calc}
  1342.  
  1343. { ------------------------------------------------------------------------ }
  1344.  
  1345. procedure CalcAndStore(MyFormula:string;StoreID:str20;var MyError:byte);
  1346.  
  1347. { this routine will evaluate an expression containing variables
  1348.   and will store the result in the variable with the ID, StoreID.
  1349.   this routine calls Calculate to evaluate the expression.
  1350.  
  1351.   MyError will be 0 for a successful evaluation.
  1352. }
  1353.  
  1354. var
  1355.  
  1356.   MyResult : real;
  1357.  
  1358. begin
  1359.  
  1360.   {call Calculate to evaluate expression}
  1361.   Calculate(MyFormula,MyResult,MyError);
  1362.  
  1363.   if MyError=0 then
  1364.     StoreVariable(StoreID,MyResult);
  1365.  
  1366. end; {CalcAndStore}
  1367.  
  1368. { ------------------------------------------------------------------------ }
  1369.  
  1370. (* This is the original main program block, now unused. --- DJF
  1371.  
  1372. BEGIN
  1373.     REPEAT
  1374.       WRITELN(OUTPUT,' ');
  1375.       WRITE(OUTPUT,'Expression (RETURN to exit)?  ');
  1376.       READLN(INPUT,expression);
  1377.       expression_length:=LENGTH(expression);
  1378.       IF expression_length > 0 THEN
  1379.         BEGIN
  1380.           error_detected:=FALSE;
  1381.           expression_index:=1;
  1382.           result:=simple_expression;
  1383.           IF error_detected THEN
  1384.             output_error(error_msg,expression,expression_index)
  1385.           ELSE
  1386.             BEGIN
  1387.               eat_leading_spaces;
  1388.               IF expression_index <= expression_length THEN
  1389.                 output_error(
  1390.                  'Error:  expression followed by garbage',
  1391.                  expression,expression_index)
  1392.               ELSE
  1393.                 output_value(result)
  1394.             END
  1395.         END
  1396.     UNTIL (expression_length = 0)
  1397.   END.
  1398.  
  1399.   *)
  1400.  
  1401. { ------------------------------------------------------------------------ }
  1402.  
  1403. Begin  {init code}
  1404.  
  1405.   {set up linked list to empty state}
  1406.  
  1407.   HPtr := nil;
  1408.   TPtr := nil;
  1409.   SPtr := nil;
  1410.  
  1411.   CalcError := 0;
  1412.  
  1413. End.   {init code}
  1414.  
  1415.